%% @doc RabbitMQ插件的工具模块。
%% 提供一些通用的辅助函数，供插件其他模块使用。
%% 对于Java开发者：可以将其看作一个包含静态工具方法的 Utility 类。
-module(emqx_plugin_rabbitmq_util).

%% 引入EMQX日志头文件，虽然本模块当前未使用日志宏，但通常好的实践是包含它以备将来使用。
-include_lib("emqx/include/logger.hrl").

%% @doc 导出的公共函数列表。
%% 这些函数可以被其他模块调用。
-export([
    client_id/1     %% 生成RabbitMQ客户端ID
    , hosts/1         %% 解析主机列表配置
    , auth_info/2      %% 生成认证信息
    , workers_tab/1   %% 根据Topic生成workers表名
    , render/2        %% 渲染消息模板
]).

%% @doc 生成RabbitMQ客户端ID的原子。
%% ClientId: 输入的客户端标识，可以是二进制、列表（字符串）或原子。
%% 返回一个形如 'emqx_plugin:rabbitmq_client:OriginalClientId' 的原子。
%% 在Erlang中，原子通常用于标识符或标签，具有比较效率高的优点。
%% <<"prefix:", (Var)/binary>> 是二进制串拼接的语法。
%% erlang:binary_to_atom(Binary, utf8) 将UTF-8编码的二进制串转换为原子。
%% Java类比: public static String generateRabbitMqClientId(String originalClientId) { return "emqx_plugin:rabbitmq_client:" + originalClientId; } (但这里返回的是原子，不是字符串)
client_id(ClientId) ->
    erlang:binary_to_atom(<<"emqx_plugin:rabbitmq_client:", (bin(ClientId))/binary>>, utf8).

%% @doc 将输入转换为二进制格式 (内部辅助函数，未导出但被client_id/1和workers_tab/1使用)。
%% 通过函数多子句和卫语句 (when) 实现不同类型的转换。
%% Java类比: 一系列重载方法或一个带类型检查的转换方法。

%% 如果输入已经是二进制，直接返回。
bin(Bin) when is_binary(Bin) -> Bin;
%% 如果输入是列表 (通常是字符串)，转换为二进制。
bin(Str) when is_list(Str) -> list_to_binary(Str);
%% 如果输入是原子，转换为UTF-8编码的二进制。
bin(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8).

%% @doc 解析主机配置列表。
%% Hosts: 可能是字符串形式的 "host1:port1,host2:port2"，或者已经是解析后的Map列表。
%% emqx_schema:parse_servers/2 会将其解析为一个标准格式的Map列表，每个Map包含 hostname 和 port。
%% lists:map/2 遍历这个列表，对每个元素应用匿名函数。
%% 匿名函数 fun(#{hostname := Host, port := Port}) -> {Host, Port} end 将每个Map转换为 {HostString, PortInteger} 元组。
%% 返回一个 {Host, Port} 元组的列表，例如 [{<<"127.0.0.1">>, 5672}, {<<"server2">>, 5672}]。
%% Java类比: 解析一个逗号分隔的服务器地址字符串，并将其转换为 List<ServerAddress>，其中 ServerAddress 是一个包含host和port的DTO。
hosts(Hosts) ->
    lists:map(
        fun(#{hostname := Host, port := Port}) -> {Host, Port} end, %% 匿名函数，提取hostname和port
        emqx_schema:parse_servers(Hosts, #{default_port => 5672}) %% 解析服务器列表，默认端口5672
    ).

%% @doc 生成RabbitMQ的认证信息。
%% Username: RabbitMQ的用户名。
%% Password: RabbitMQ的密码 (可能是加密的)。
%% 返回一个Map，包含 'username' 和 'password'。

%% 子句1: 如果Username是空二进制串 (表示未配置)，则返回空Map (无认证)。
auth_info(<<>>, _) ->
    #{};
%% 子句2: 如果Username是二进制串 (已配置)。
auth_info(Username, Password) when is_binary(Username) ->
    #{ %%
        username => Username,
        %% emqx_secret:unwrap(Password) 用于解密或获取原始的Password。
        %% emqx_maybe:define(Value, Default) 如果Value是undefined，则返回Default (空二进制串)。
        password => emqx_maybe:define(emqx_secret:unwrap(Password), <<>>)
    };
%% 子句3: 其他情况 (例如Username不是二进制也不是空)，返回空Map。
auth_info(_, _) ->
    #{}.

%% @doc 根据Topic名称生成一个用于workers ETS表的原子名称。
%% Topic: RabbitMQ的主题名称，可以是二进制、列表（字符串）或原子。
%% 返回一个形如 'emqx_plugin_rabbitmq_OriginalTopic' 的原子。
%% 这通常用于动态创建或查找与特定主题相关的ETS表。
%% Java类比: public static String generateWorkerTableName(String topic) { return "emqx_plugin_rabbitmq_" + topic.replace("/", "_"); } (同样，Erlang返回原子)
workers_tab(Topic) ->
    erlang:binary_to_atom(<<"emqx_plugin_rabbitmq_", (bin(Topic))/binary>>, utf8).

%% @doc 使用Message中的数据渲染Template字符串。
%% Template: 包含占位符的字符串模板 (二进制格式)。
%% Message: 一个Map，包含了用于替换占位符的数据。
%% Opts: 传递给 emqx_placeholder:proc_tmpl/3 的选项Map。
%%   var_trans: 一个匿名函数，用于转换模板中变量的值。
%%     - undefined 原子会被转换为空二进制串 <<"">>。
%%     - 其他值 X 会通过 emqx_utils_conv:bin(X) 转换为二进制格式。
%%   return => full_binary: 指定返回结果是完整的二进制串。
%% emqx_placeholder:proc_tmpl/3 是EMQX提供的模板处理函数。
%% Java类比: 使用类似Apache Commons Text的StringSubstitutor或者一个简单的模板引擎，
%%            对模板 "Hello ${user}" 和数据 Map.of("user", "World") 进行处理，得到 "Hello World"。
render(Template, Message) ->
    Opts = #{ %%
        var_trans => fun %% 定义变量转换函数
                         (undefined) -> <<"">>; %% undefined 转换为 空二进制
                         (X) -> emqx_utils_conv:bin(X) %% 其他值统一转为二进制
                     end,
        return => full_binary %% 要求返回完整的二进制结果
    },
    emqx_placeholder:proc_tmpl(Template, Message, Opts).